小編先前每個範例都有提供服務(Service)層級的測試案例,但部分開發者會開發許多控制器(Controller),除了可透過小編所帶出的Swagger頁面或Postman工具進行觸發API測試,那如果要走入自動化測試必須獨立一個測試區塊,故小編提出透過Spring 核心所提供的MockMvc進行觸發各API及進行驗證與測試,小編今天將提供根據HTTP API的回覆狀態碼進行驗證,即根據獲取的回覆實體內容(Response Entity)進行資料筆數及內容驗證,並會採用預設的產生其測試報告給開發者做確認,是一套相當不錯的測試套件,相關細節各位請看原理介紹。
MockMvc是基於Spring Boot的測試框架,再運用此框架之前須事先配置好三項設定,分別為:1. @RunWith(SpringJUnit4ClassRunner.class),讓測試運行於Spring測試環境。2. @SpringBootTest(classes = ApplicationBoot.class):提供系統的Spring Boot專案的啟動位置。3. @ActiveProfiles({"stag","native"}):配置測試環境位置(dev|stag|prod)組態資訊。在初始化MockMvc測試套件時,因需要其Web API 呼叫需要用到ServletContext實例,而注入後的WebApplicationContext已包含此Web容器,故會將此WebApplicationContext設置入MockMvcBuilders.webAppContextSetup中,即可完成初始化,範例程式碼如下。
- Initialize MockMvc Tool
@Ignore
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ApplicationBoot.class)
@ActiveProfiles({"stag","native"})
public class ControllerTestBase extends TestCase {
protected MockMvc mvc;
@Autowired
private WebApplicationContext webApplicationContext;
public ControllerTestBase() {}
Logger logger = LoggerFactory.getLogger(ControllerTestBase.class);
@Before
public void init() {
logger.info(this.getClass().getName());
logger.info("------- init test mock API WebApplicationContext -------");
this.mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
logger.info("------- init finished test mock API WebApplicationContext --------");
logger.info("***** API Mock Start *****");
}
}
- 擴展初始化測試控制器(ControllerTestBase),並延伸配置其MockMvc配件
public class TaiwanProductTestSuite extends ControllerTestBase {
.....
.....
.....
}
- 測試GET方法,透過mvc.perform設置MockMvcRequestBuilders.get("/v1/taiwan/list")方法,並配置為JSON格式進行處理,並驗證其狀態碼及內容資訊。
@Test
@Order(1)
public void listSeaFoodTask() throws Exception{
MvcResult mvcResult = this.mvc.perform(MockMvcRequestBuilders.get("/v1/taiwan/list")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON_VALUE))
.andReturn();
int status = mvcResult.getResponse().getStatus();
System.out.println("Http status code : " + status);
assertEquals(200,status);
String responseText = mvcResult.getResponse().getContentAsString();
System.out.println("Response result : " + responseText);
List<SeaFood> seaFoods = new Gson().fromJson(responseText,List.class);
assertEquals(seaFoods.size(),3);
System.out.println("[ TEST CASE ] - Verify [GET - TAIWAN] list sea food products SUCCESS.....!");
}
3-1. 驗證其結果
16:56:07.779 INFO 39884 --- [ Test worker] s.s.s.controller.ControllerTestBase : ------- init test mock API WebApplicationContext -------
16:56:07.784 INFO 39884 --- [ Test worker] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
16:56:07.784 INFO 39884 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet ''
16:56:07.785 INFO 39884 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 1 ms
16:56:07.786 INFO 39884 --- [ Test worker] s.s.s.controller.ControllerTestBase : ------- init finished test mock API WebApplicationContext --------
16:56:07.786 INFO 39884 --- [ Test worker] s.s.s.controller.ControllerTestBase : ***** API Mock Start *****
Http status code : 200
Response result : [{"id":"C-0002","name":"Snow Crab","description":"Opilio is the primary species referred to as snow crab.","price":350},{"id":"F-0001","name":"Dragon fish","description":"Gold type for the Dragon fish.","price":250},{"id":"C-0001","name":"King Crab","description":"A taxon of crab-like decapod crustaceans chiefly found in cold seas.","price":300}]
[ TEST CASE ] - Verify [GET - TAIWAN] list sea food products SUCCESS.....!
16:56:07.950 INFO 39884 --- [ Test worker] s.s.s.controller.ControllerTestBase : ***** API Mock End *****
- 測試CREATE方法,透過mvc.perform設置MockMvcRequestBuilders.post("/v1/taiwan/create")方法,並配置請求內容(RequestBody)為convertJsonRequestBody(seaFood))及透過JSON格式進行處理,並驗證其狀態碼及內容資訊。
@Test
@Order(3)
public void createSeaFoodTask() throws Exception {
SeaFood seaFood = new SeaFood()
.setId("F-0999")
.setName("WEI WEI Crab")
.setDescription("Opilio is the primary species referred to as china WEI WEI crab.")
.setPrice(555);
MvcResult mvcResult = this.mvc.perform(MockMvcRequestBuilders.post("/v1/taiwan/create")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(convertJsonRequestBody(seaFood))
.accept(MediaType.APPLICATION_JSON_VALUE))
.andReturn();
int status = mvcResult.getResponse().getStatus();
System.out.println("Http status code : " + status);
assertEquals(201,status);
String responseText = mvcResult.getResponse().getContentAsString();
System.out.println("Response result : " + responseText);
SeaFood responseBody = new Gson().fromJson(responseText,SeaFood.class);
assertEquals(responseBody.getName(),"WEI WEI Crab");
assertEquals(responseBody.getId(),"F-0999");
assertEquals(responseBody.getDescription(),"Opilio is the primary species referred to as china WEI WEI crab.");
assertEquals(responseBody.getPrice(),555);
System.out.println("[ TEST CASE ] - Verify [CREATE - TAIWAN] sea food SUCCESS .....!");
}
4-1. 驗證其結果
17:02:27.524 INFO 39900 --- [ Test worker] s.s.s.controller.ControllerTestBase : ------- init test mock API WebApplicationContext -------
17:02:27.537 INFO 39900 --- [ Test worker] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
17:02:27.538 INFO 39900 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet ''
17:02:27.539 INFO 39900 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 1 ms
17:02:27.539 INFO 39900 --- [ Test worker] s.s.s.controller.ControllerTestBase : ------- init finished test mock API WebApplicationContext --------
17:02:27.540 INFO 39900 --- [ Test worker] s.s.s.controller.ControllerTestBase : ***** API Mock Start *****
17:02:27.681 INFO 39900 --- [ Test worker] s.s.s.s.SeaFoodRetailerServiceImpl : Create Taiwan product success !
Http status code : 201
Response result : {"id":"F-0999","name":"WEI WEI Crab","description":"Opilio is the primary species referred to as china WEI WEI crab.","price":555}
[ TEST CASE ] - Verify [CREATE - TAIWAN] sea food SUCCESS .....!
17:02:27.704 INFO 39900 --- [ Test worker] s.s.s.controller.ControllerTestBase : ***** API Mock End *****
- 測試PUT方法,透過mvc.perform設置MockMvcRequestBuilders.put("/v1/taiwan/update")方法,並配置請求內容(RequestBody)為convertJsonRequestBody(seaFood))及透過JSON格式進行處理,並驗證其狀態碼及內容資訊。
SeaFood seaFood = new SeaFood()
.setId("C-0002")
.setName("Snow Crab")
.setDescription("Opilio is the primary species referred to as snow crab.")
.setPrice((int)(350*0.8));
MvcResult mvcResult = this.mvc.perform(MockMvcRequestBuilders.put("/v1/taiwan/update")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(convertJsonRequestBody(seaFood))
.accept(MediaType.APPLICATION_JSON_VALUE))
.andReturn();
int status = mvcResult.getResponse().getStatus();
System.out.println("Http status code : " + status);
assertEquals(200,status);
String responseText = mvcResult.getResponse().getContentAsString();
System.out.println("Response result : " + responseText);
SeaFood responseBody = new Gson().fromJson(responseText,SeaFood.class);
assertEquals(responseBody.getName(),"Snow Crab");
assertEquals(responseBody.getId(),"C-0002");
assertEquals(responseBody.getDescription(),"Opilio is the primary species referred to as snow crab.");
assertEquals(responseBody.getPrice(),280);
System.out.println("[ TEST CASE ] - Verify [UPDATE - TAIWAN] sea food SUCCESS .....!");
5-1. 驗證其結果
17:04:54.342 INFO 39908 --- [ Test worker] s.s.s.controller.ControllerTestBase : ------- init test mock API WebApplicationContext -------
17:04:54.352 INFO 39908 --- [ Test worker] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
17:04:54.353 INFO 39908 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet ''
17:04:54.354 INFO 39908 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 1 ms
17:04:54.355 INFO 39908 --- [ Test worker] s.s.s.controller.ControllerTestBase : ------- init finished test mock API WebApplicationContext --------
17:04:54.356 INFO 39908 --- [ Test worker] s.s.s.controller.ControllerTestBase : ***** API Mock Start *****
Http status code : 200
Response result : {"id":"C-0002","name":"Snow Crab","description":"Opilio is the primary species referred to as snow crab.","price":280}
[ TEST CASE ] - Verify [UPDATE - TAIWAN] sea food SUCCESS .....!
17:04:54.531 INFO 39908 --- [ Test worker] s.s.s.controller.ControllerTestBase : ***** API Mock End *****
- 測試DELETE方法,透過mvc.perform設置MockMvcRequestBuilders.delete("/v1/taiwan/remove/{id}","F-0001")方法,並透過JSON格式進行處理,並驗證其狀態碼及內容資訊。
MvcResult mvcResult = this.mvc.perform(MockMvcRequestBuilders.delete("/v1/taiwan/remove/{id}","F-0001")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON_VALUE))
.andReturn();
int status = mvcResult.getResponse().getStatus();
System.out.println("Http status code : " + status);
assertEquals(200,status);
Boolean isDelete = Boolean.valueOf(mvcResult.getResponse().getContentAsString());
System.out.println("Response result : " + isDelete);
assertTrue(isDelete);
System.out.println("[ TEST CASE ] - Verify [DELETE - TAIWAN] sea food SUCCESS .....!");
5-1. 驗證及結果
17:08:26.229 INFO 39915 --- [ Test worker] s.s.s.controller.ControllerTestBase : ------- init test mock API WebApplicationContext -------
17:08:26.239 INFO 39915 --- [ Test worker] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
17:08:26.239 INFO 39915 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet ''
17:08:26.240 INFO 39915 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 1 ms
17:08:26.241 INFO 39915 --- [ Test worker] s.s.s.controller.ControllerTestBase : ------- init finished test mock API WebApplicationContext --------
17:08:26.241 INFO 39915 --- [ Test worker] s.s.s.controller.ControllerTestBase : ***** API Mock Start *****
Http status code : 200
Response result : true
[ TEST CASE ] - Verify [DELETE - TAIWAN] sea food SUCCESS .....!
17:08:26.311 INFO 39915 --- [ Test worker] s.s.s.controller.ControllerTestBase : ***** API Mock End *****
完成以上測試流程後,小編提供的測試報告可取得每個測試套裝的測試API數量,並看取最終測試結果,相關測試結果如下。
Run test task
gradle test
Run open result html
open ./build/reports/tests/test/index.html
API Spec test report
Mind-blowing test Staging environment detail
SpringMvc框架MockMvc單元測試註解及其原理分析